//
//  CollectionExample.swift
//  SwiftCode
//
//  Created by 于虎拴 on 2023/3/1.
//

import Foundation

/// 序列：一种提供对其元素的顺序、迭代访问的类型。
extension Sequence {}

/// 其元素可以非破坏性地多次遍历并通过索引下标访问的序列。
extension Collection {}

extension Array { }
extension Set {}
extension Dictionary {}

final class CollectionExample: ListViewController {
  override func getItemCount() -> Int {
    data.count
  }
  
  override func getItem(_ indexPath: IndexPath) -> Item {
    data[indexPath.row]
  }
  
  private lazy var data: [Item] = [
    .init(title: "Creating an Arrays", description: "数组初始化", action: arrays),
    .init(title: "Array Are Value Type", description: "数组为值类型", action: arrayValueType),
    .init(title: "Accessing & Modifying an Array", description: "Array的访问和修改", action: arrayAccessing),
    .init(title: "Iteration Over an Array", description: "遍历数组", action: arrayIteration),
    .init(title: "Creating a Set", description: "Set 的初始化", action: initSet),
    .init(title: "Accessing & Modifying a Set", description: "Set的访问和修改", action: accessingSet),
    .init(title: "Iteration Over a Set", description: "遍历Set", action: iterationSet),
    .init(title: "Fundamental Set Operations", description: "Set基本运算", action: operationsSet),
    .init(title: "Set Membership and Equality", description: "Set成员关系", action: membershipSet),
    .init(title: "Creating a Dictionary", description: "Dictionary初始化", action: initDictionary),
    .init(title: "Accessing & Modifying a Dictionary", description: "Dictionary的访问和修改", action: accessingDictionary),
    .init(title: "Iterating Over a Dictionary", description: "Dictionary遍历", action: iterationDictionary),
  ]
}

private func arrays() {
  // 您可以使用初始化语法创建特定类型的空数组：
  var someInts: [Int] = []
  print("someInts is of type [Int] with \(someInts.count) items.")
  // Prints "someInts is of type [Int] with 0 items."
  
  someInts.append(3)
  // someInts now contains 1 value of type Int
  print(someInts)
  
  someInts = []
  // someInts is now an empty array, but is still of type [Int]
  print(someInts)
  
  let threeDoubles = Array(repeating: 0.0, count: 3)
  // threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
  print(threeDoubles)
  
  // 通过将两个数组相加来创建数组
  let anotherThreeDoubles = Array(repeating: 2.5, count: 3)
  // anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5]
  print(anotherThreeDoubles)

  let sixDoubles = threeDoubles + anotherThreeDoubles
  // sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
  print(sixDoubles)
  
  let shoppingList: [String] = ["Eggs", "Milk"]
  // shoppingList has been initialized with two initial items
  print(shoppingList)
}

private func arrayValueType() {
  let shoppingList = ["Eggs", "Milk"]
  // 验证 Array 为值类型
  var copyShoppingList = shoppingList
  copyShoppingList += ["Rice"]
  print("原数组：", shoppingList)
  print("新数组：", copyShoppingList)
}

private func arrayAccessing() {
  var shoppingList = ["Eggs", "Milk"]
  // 访问和修改数组
  print("The shopping list contains \(shoppingList.count) items.")
  // Prints "The shopping list contains 2 items."
  
  if shoppingList.isEmpty {
    print("The shopping list is empty.")
  } else {
    print("The shopping list isn't empty.")
  }
  // Prints "The shopping list isn't empty."
  
  shoppingList.append("Flour")
  // shoppingList now contains 3 items, and someone is making pancakes
  print(shoppingList)
  
  shoppingList += ["Baking Powder"]
  // shoppingList now contains 4 items
  shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
  // shoppingList now contains 7 items
  print(shoppingList)
  
  let firstItem = shoppingList[0]
  // firstItem is equal to "Eggs"
  print(firstItem)
  
  shoppingList[0] = "Six eggs"
  // the first item in the list is now equal to "Six eggs" rather than "Eggs"
  
  print(shoppingList)
  shoppingList[4...6] = ["Bananas", "Apples"]
  // shoppingList now contains 6 items
  print(shoppingList)
  
  shoppingList.insert("Maple Syrup", at: 0)
  // shoppingList now contains 7 items
  // "Maple Syrup" is now the first item in the list
  print(shoppingList)
  
  _ = shoppingList.remove(at: 0)
  // the item that was at index 0 has just been removed
  // shoppingList now contains 6 items, and no Maple Syrup
  // the mapleSyrup constant is now equal to the removed "Maple Syrup" string
  print(shoppingList)
  
  _ = shoppingList.removeLast()
  // the last item in the array has just been removed
  // shoppingList now contains 5 items, and no apples
  // the apples constant is now equal to the removed "Apples" string
  print(shoppingList)
}

private func arrayIteration() {
  let shoppingList = ["Six eggs", "Milk", "Flour", "Baking Powder", "Bananas"]
  for item in shoppingList {
      print(item)
  }
  // Six eggs
  // Milk
  // Flour
  // Baking Powder
  // Bananas
  
  for (index, value) in shoppingList.enumerated() {
      print("Item \(index + 1): \(value)")
  }
  // Item 1: Six eggs
  // Item 2: Milk
  // Item 3: Flour
  // Item 4: Baking Powder
  // Item 5: Bananas
}

private func initSet() {
  var letters = Set<Character>()
  print("letters is of type Set<Character> with \(letters.count) items.")
  // Prints "letters is of type Set<Character> with 0 items."
  
  letters.insert("a")
  // letters now contains 1 value of type Character
  letters = []
  // letters is now an empty set, but is still of type Set<Character>
  print("letters is of type Set<Character> with \(letters.count) items.")
}

private func accessingSet() {
  var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
  print("I have \(favoriteGenres.count) favorite music genres.")
  // Prints "I have 3 favorite music genres."
  
  if favoriteGenres.isEmpty {
      print("As far as music goes, I'm not picky.")
  } else {
      print("I have particular music preferences.")
  }
  // Prints "I have particular music preferences."
  
  favoriteGenres.insert("[Tool J]")
  // favoriteGenres now contains 4 items
  
  if let removedGenre = favoriteGenres.remove("Rock") {
      print("\(removedGenre)? I'm over it.")
  } else {
      print("I never much cared for that.")
  }
  // Prints "Rock? I'm over it."
  
  if favoriteGenres.contains("Funk") {
      print("I get up on the good foot.")
  } else {
      print("It's too funky in here.")
  }
  // Prints "It's too funky in here."
  print(favoriteGenres)
}

private func iterationSet() {
  let favoriteGenres: Set = ["Hip hop", "[Tool J]", "Classical"]
  for genre in favoriteGenres {
      print("\(genre)")
  }
  // Classical
  // [Tool J]
  // Hip hop
  
  for genre in favoriteGenres.sorted() {
      print("\(genre)")
  }
  // Classical
  // Hip hop
  // [Tool J]
}

private func operationsSet() {
  let oddDigits: Set = [1, 3, 5, 7, 9]
  let evenDigits: Set = [0, 2, 4, 6, 8]
  let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
  
  print(oddDigits.union(evenDigits).sorted())
  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  
  print(oddDigits.intersection(evenDigits).sorted())
  // []
  print(oddDigits.subtracting(singleDigitPrimeNumbers).sorted())
  // [1, 9]
  print(oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted())
  // [1, 2, 9]
}

private func membershipSet() {
  let houseAnimals: Set = ["🐶", "🐱"]
  let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
  let cityAnimals: Set = ["🐦", "🐭"]
  print("houseAnimals", houseAnimals)
  print("farmAnimals", farmAnimals)
  print("cityAnimals", cityAnimals)

  print(houseAnimals.isSubset(of: farmAnimals))
  // true
  print(farmAnimals.isSuperset(of: houseAnimals))
  // true
  print(farmAnimals.isDisjoint(with: cityAnimals))
  // true
}

private func initDictionary() {
  var namesOfIntegers: [Int: String] = [:]
  // namesOfIntegers is an empty [Int: String] dictionary
  
  namesOfIntegers[16] = "sixteen"
  // namesOfIntegers now contains 1 key-value pair
  print(namesOfIntegers)
  
  namesOfIntegers = [:]
  // namesOfIntegers is once again an empty dictionary of type [Int: String]
  print(namesOfIntegers)
  
  let airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
  print(airports)
}

private func accessingDictionary() {
  var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
  
  print("The airports dictionary contains \(airports.count) items.")
  // Prints "The airports dictionary contains 2 items."
  
  if airports.isEmpty {
      print("The airports dictionary is empty.")
  } else {
      print("The airports dictionary isn't empty.")
  }
  // Prints "The airports dictionary isn't empty."
  
  airports["LHR"] = "London"
  // the airports dictionary now contains 3 items
  print(airports)
  
  airports["LHR"] = "London Heathrow"
  // the value for "LHR" has been changed to "London Heathrow"
  print(airports)
  
  if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
      print("The old value for DUB was \(oldValue).")
  }
  // Prints "The old value for DUB was Dublin."
  print(airports)
  
  if let airportName = airports["DUB"] {
      print("该机场名字是 \(airportName).")
  } else {
      print("该机场不在机场字典中。")
  }
  // Prints "The name of the airport is Dublin Airport."
  
  airports["APL"] = "Apple International"
  // "Apple International" isn't the real airport for APL, so delete it
  print(airports)
  
  airports["APL"] = nil
  // APL has now been removed from the dictionary
  print(airports)
  
  if let removedValue = airports.removeValue(forKey: "DUB") {
      print("删除机场的名字是 \(removedValue).")
  } else {
      print("机场字典不包含 DUB 的值。")
  }
  // Prints "The removed airport's name is Dublin Airport."
  print(airports)
}

private func iterationDictionary() {
  let airports = ["YYZ": "Toronto Pearson", "LHR": "London Heathrow"]
  for (airportCode, airportName) in airports {
      print("\(airportCode): \(airportName)")
  }
  // LHR: London Heathrow
  // YYZ: Toronto Pearson
  
  for airportCode in airports.keys {
      print("Airport code: \(airportCode)")
  }
  // Airport code: LHR
  // Airport code: YYZ

  for airportName in airports.values {
      print("Airport name: \(airportName)")
  }
  // Airport name: London Heathrow
  // Airport name: Toronto Pearson
  
  let airportCodes: Array = [String](airports.keys)
  // airportCodes is ["LHR", "YYZ"]
  print(airportCodes)

  let airportNames: Set = Set<String>(airports.values)
  // airportNames is ["London Heathrow", "Toronto Pearson"]
  print(airportNames)
}
